/*
 * Copyright (C) 2008 CERNET Network Center 
 * dvping/dvmcast/hdvping/hdvmcast series 
 * 
 * Design and coding: 
 *  	Xing Li <xing@cernet.edu.cn> 
 *	Congxiao Bao <congxiao@cernet.edu.cn>
 * 	Jinpeng Jiang <jjp02@mails.tsinghua.edu.cn>
 * 
 * Contributions:
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 */


#include <iomanip>
using namespace std;
#include <Socket.h>
#include <StringUtil.h>
#include <ThreadUtil.h>
using namespace kascale;
#include "dvping.h"

void PrintUsage()
{
	cout << "Usage:dvping [-h] [-p snd_port] [-rp rcv_port] [-t period] [-tt tt] [-bw bandwidth] [-rh remote_host_addr] host" << endl;
}


string bandwidth_to_string(double bw)
{
	ostringstream ostr;
	string unit;
	if(bw > 1e12)
	{
		bw /= 1e12;
		unit = "T";
	}
	else if(bw > 1e9)
	{
		bw /= 1e9;
		unit = "G";
	}
	else if(bw > 1e6)
	{
		bw /= 1e6;
		unit = "M";
	}
	else if(bw > 1e5)
	{
		bw /= 1e3;
		unit = "K";
	}
	ostr << setprecision(3) <<  bw << unit;
	return ostr.str();
}

int main(int argc, char* argv[])
{
	Socket::Init();
	IPAddress host,remote_host;
	bool have_remote=false;
	in_port_t snd_port = 8000;
	in_port_t rcv_port = 8000;
	int32_t	  period = 10;
	double	  bandwidth = 0.0;
	int32_t	  ping_interval = period;

	// get host
	if(argc < 2)
	{
		cout << "no host" << endl;
		PrintUsage();
		return 1;
	}
    for(int i = 1; i < argc; ++i)
	{
		string arg = argv[i];
		if(arg[0] != '-')
		{
			if(!ParseString(arg, host))
			{
				cout << "Invalid host:" << arg << endl;
				PrintUsage();
				return 1;
			}
		}
		else if(arg == "-rh")
		{
			++i;
			if(i<argc)
			{
				if(!ParseString(argv[i], remote_host))
				{
					cout << "Invalid remote host:" << arg << endl;
					PrintUsage();
					return 1;
				}
				else
					have_remote=true;
			}
			else
			{
				cout <<"no remote host" <<endl;
			}

		}
		else if(arg == "-h")
		{
			PrintUsage();
			return 0;
		}
		else if(arg == "-p")
		{
			++i;
			if(i < argc)
			{
				if(!ParseString(argv[i], snd_port))
				{
					cout << "Invalid send port:" << argv[i] << endl;
					PrintUsage();
					return 1;
				}
				if(snd_port == 0)
				{
					cout << "Send port out of range:" << argv[i] << endl;
					PrintUsage();
					return 1;
				}
			}
			else
			{
				cout << "no snd_port" << endl;
				PrintUsage();
				return 1;
			}
		}
		else if(arg == "-rp")
		{
			++i;
			if(i < argc)
			{
				if(!ParseString(argv[i], rcv_port))
				{
					cout << "Invalid recv port:" << argv[i] << endl;
					PrintUsage();
					return 1;
				}
				if(rcv_port == 0)
				{
					cout << "Recv port out of range:" << argv[i] << endl;
					PrintUsage();
					return 1;
				}
			}
			else
			{
				cout << "no rcv_port" << endl;
				PrintUsage();
				return 1;
			}
		}
		else if(arg == "-t")
		{
			++i;
			if(i < argc)
			{
				if(!ParseString(argv[i], period))
				{
					cout << "Invalid period:" << argv[i] << endl;
					PrintUsage();
					return 1;
				}
				if(period <= 0)
				{
					cout << "Period should be greater than zero:" << argv[i] << endl;
					return 1;
				}
			}
			else
			{
				cout << "no period" << endl;
				PrintUsage();
				return 1;
			}
		}
		else if(arg == "-tt")
		{
			++i;
			if(i < argc)
			{
				if(!ParseString(argv[i], ping_interval))
				{
					cout << "Invalid ping interval." << argv[i] << endl;
					PrintUsage();
					return 1;
				}
				if(ping_interval <= 0)
				{
					cout << "Ping interval should be greater than zero:" << argv[i] << endl;
					return 1;
				}
			}
			else
			{
				cout << "no ping interval." << endl;
				PrintUsage();
				return 1;
			}
		}
		else if(arg == "-bw")
		{
			++i;
			if(i < argc)
			{
				istringstream istr(argv[i]);
				istr >> bandwidth;
				if(!istr)
				{
					cout << "Invalid bandwidth:" << argv[i] << endl;
					PrintUsage();
					return 1;
				}
				if(bandwidth <= 0)
				{
					cout << "bandwidth should be greater than zero:" << argv[i] << endl;
					PrintUsage();
					return 1;
				}
				string unit;
				istr >> unit;
				if(istr && !unit.empty())
				{
					tolower(unit);
                    switch(unit[0])
					{
					case 't':
						bandwidth *= 1e12;
						break;
					case 'g':
						bandwidth *= 1e9;
						break;
					case 'm':
						bandwidth *= 1e6;
						break;
					case 'k':
						bandwidth *= 1e3;
						break;
					default:
						cout << "Unknown bandwidth unit:" << unit << endl;
						PrintUsage();
						return 1;
					}
				}
			}
			else
			{
				cout << "no bandwidth" << endl;
				PrintUsage();
				return 1;
			}
		}
	}
	if(host == IPAddress::Null)
	{
		cout << "no host" << endl;
		PrintUsage();
		return 0;
	}
	dv_ping_result dpr;
	if(!have_remote)
		remote_host=host;
	DVPinger pinger(host, remote_host, snd_port, rcv_port);
	if(!pinger.Init())
	{
		cout << "dvping initialization failed." << endl;
		return 1;
	}
	cout << "ping " << host << " with dvts data:"<< endl;
	cout << "send port:" << snd_port << endl;
	cout << "recv port:" << rcv_port << endl;
	cout << "period   :" << period << " s" << endl;

	cout << "snd_pkt  rcv_pkt  snd_rate     rcv_rate     loss      rtt" << endl;
	if(bandwidth == 0.0)
	{
		for(bandwidth = 1e6; bandwidth < 33e6; bandwidth *= 2)
		{
			for(int32_t time_past = 0; time_past < period; time_past += ping_interval)
			{
				dpr = pinger.Ping(ping_interval * 1000000LL, bandwidth / 8e6);
				cout << FormatOutput(dpr.packets_sent, 9) << FormatOutput(dpr.packets_recv, 9)
					<< FormatOutput(bandwidth_to_string(dpr.bytes_sent_ideal * 8000000.0 / dpr.period) + "bps", 13)
					<< FormatOutput(bandwidth_to_string(dpr.bytes_recv * 8000000.0 / dpr.period) + "bps", 13);
				ostringstream ostr;
				ostr << setprecision(3) << (dpr.packets_sent - dpr.packets_recv) * 100.0 / dpr.packets_sent << "%";
				cout << FormatOutput(ostr.str(), 10);
				ostr.str("");
				ostr.clear();
				if(dpr.delay < 0)
					ostr << "--";
				else
					ostr << setprecision(4) << dpr.delay / 1000.0 << "ms";
				cout << ostr.str() << endl;
			}
		} 
	}
	else
	{
		for(int32_t time_past = 0; time_past < period; time_past += ping_interval)
		{
			dpr = pinger.Ping(ping_interval * 1000000LL, bandwidth / 8000000.0);
			
			cout << FormatOutput(dpr.packets_sent, 9) << FormatOutput(dpr.packets_recv, 9)
				<< FormatOutput(bandwidth_to_string(dpr.bytes_sent_ideal * 8000000.0 / dpr.period) + "bps", 13)
				<< FormatOutput(bandwidth_to_string(dpr.bytes_recv * 8000000.0 / dpr.period) + "bps", 13);
			ostringstream ostr;
			ostr << setprecision(3) << (dpr.packets_sent - dpr.packets_recv) * 100.0 / dpr.packets_sent << "%";
			cout << FormatOutput(ostr.str(), 10);
			ostr.str("");
			ostr.clear();
			if(dpr.delay < 0)
				ostr << "--"; 
			else
				ostr << setprecision(4) << dpr.delay / 1000.0 << "ms"; 
			cout << ostr.str() << endl;
		}
	}
	pinger.Cleanup();
	Socket::Cleanup();
	return 0;
}

